<?php
/**
 * @copyright Copyright (c) 2025 
 * @license https://opensource.org/licenses/GPL-3.0
 * @link 
 */

declare (strict_types = 1);

namespace app\admin\controller;

use app\admin\BaseController;
use app\admin\model\File;
use think\exception\ValidateException;
use think\facade\Db;
use think\facade\View;

class Files extends BaseController
{
    protected    $baseDir = '/www/wwwroot/www.xyrakuten.com/public/images'; // 安全的基础目录（按需修改）
    protected    $searchPath = '/www/wwwroot/www.xyrakuten.com/public';
    public function index()
    {
        if (request()->isAjax()) {
            $param = get_params();
			// 配置参数
			// 获取请求的目录（相对路径）
			$requestDir = isset($param['keywords']) ? trim($param['keywords'], '/') : '';
			// 构建完整路径并安全验证
			$fullPath = realpath($this->baseDir . '/' . $requestDir);
			if ($fullPath === false || strpos($fullPath, realpath($this->baseDir)) !== 0) {
				echo json_encode([
					'status' => 'error',
					'base_dir' => '', // 可选的：显示基础目录相对路径
					'current_dir' => $requestDir ?: '/',
					'count' => 0,
					'data' => []
				]);
				exit;
			}			
			// 执行扫描并返回结果
			try {
				$fileList = $this->scanDirectory($fullPath, $this->searchPath);
				$data     = $this->searchFilesWithPagination($fileList,$param['page'],$param['limit']);
				echo json_encode($data);
			} catch (Exception $e) {
				http_response_code(500);
				echo json_encode([
					'status' => 'error',
					'message' => $e->getMessage()
				]);
			}
			exit;
			
        } else {
            return view('index', [
                'page'=>!empty($_REQUEST['page']) ? $_REQUEST['page'] : 1,
                'limit'=>!empty($_REQUEST['limit']) ? $_REQUEST['limit'] : 20,
                'keywords'=>!empty($_REQUEST['keywords']) ? $_REQUEST['keywords'].'/' : '',
                'keywords_name'=>!empty($_REQUEST['keywords']) ? $_REQUEST['keywords'] : '',
            ]);
        }
    }
	// 遍历目录函数
	private function scanDirectory($dir, $baseDir) {
		$result = [];
		$files = array_diff(scandir($dir), ['.', '..']);
		
		foreach ($files as $file) {
			$path = $dir . '/' . $file;
			$isDir = is_dir($path);
			
			$relativePath = $this->getRelativePath($path, $baseDir);
			
			$result[] = [
				'image_name' => $file,
				'relative_path' => '/'.$relativePath, // 使用相对路径
				'image_extension' => $isDir ? 'directory' : 'png',
				'size' => $isDir ? null : filesize($path),
				'mtime' => date('Y-m-d H:i:s', filemtime($path))
			];
		}
		
		return $result;
	}

	// 计算相对路径的函数
	private function getRelativePath($fullPath, $baseDir) {
		return ltrim(str_replace($baseDir, '', $fullPath), '/');
	}
	private function searchImageFilesRelative($basePath,$searchPath,$searchTitle) {
		$foundImages = [];
		
		// 规范化基础路径和搜索路径
		$realBasePath = realpath(rtrim($basePath, '/\\'));
		$realSearchPath = realpath($searchPath);
		
		// 验证路径有效性
		if (!$realBasePath || !is_dir($realBasePath) || !$realSearchPath || !is_dir($realSearchPath)) {
			return $foundImages;
		}
		
		// 确保搜索路径在基础路径内（安全限制）
		if (strpos($realSearchPath, $realBasePath) !== 0) {
			return $foundImages;
		}
		
		// 支持的图片扩展名
		$imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp', 'svg'];
		
		// 递归搜索函数
		$searchFunc = function($currentPath) use (&$searchFunc, $realBasePath, $searchTitle, $imageExtensions, &$foundImages) {
			$dir = opendir($currentPath);
			if (!$dir) return;
			
			while (false !== ($file = readdir($dir))) {
				if ($file === '.' || $file === '..') continue;
				
				$fullPath = $currentPath . DIRECTORY_SEPARATOR . $file;
				
				if (is_dir($fullPath)) {
					// 递归搜索子目录
					$searchFunc($fullPath);
				} elseif (is_file($fullPath)) {
					$fileInfo = pathinfo($fullPath);
					$extension = strtolower($fileInfo['extension'] ?? '');
					
					// 检查是否是图片文件
					if (in_array($extension, $imageExtensions)) {
						$fileName = $fileInfo['filename'];
						
						// 检查标题是否匹配
						if (stripos($fileName, $searchTitle) !== false) {
							// 计算相对路径（跨平台统一使用正斜杠）
							$relativePath = ltrim(
								str_replace('\\', '/', 
									substr($fullPath, strlen($realBasePath))
								), 
								'/'
							);
							
							$foundImages[] = [
								'relative_path' => '/'.$relativePath,  // 相对路径
								'image_name' => $fileName.'.'.$extension,         // 图片名称
								'image_extension' => $extension,   // 图片扩展名
								//'full_path' => $fullPath           // 保留完整路径（可选）
							];
						}
					}
				}
			}
			
			closedir($dir);
		};
		
		// 开始搜索
		$searchFunc($realSearchPath);
		return $foundImages;
	}
	private function searchFilesWithPagination($allFiles, $page = 1, $perPage = 20) {
		//$allFiles = $this->searchImageFilesRelative($basePath, $searchPath,$searchTitle);
		 $total = count($allFiles);
    
		// 确保参数是整数
		$page = max(1, (int)$page);
		$perPage = max(1, (int)$perPage);
		
		$totalPages = ceil($total / $perPage);
		$offset = ($page - 1) * $perPage;
		
		// 确保不会超出数组范围
		$offset = max(0, min($offset, $total));
		$length = min($perPage, $total - $offset);
		
		$paginatedImages = array_slice($allFiles, $offset, $length);
		
		return [
			'count' => $total,
			'total_pages' => ceil($total / $perPage),
			'page' => $page,
			'data' => $paginatedImages
		];
	}
    /**
     * 安全删除多个文件或文件夹（不使用SPL迭代器）
     *
     * @param array $paths 要删除的路径数组
     * @param string $baseDir 基础目录（用于安全限制）
     * @return array 删除结果
     */
    protected function deletePaths(array $paths, string $baseDir = null): array
    {
        $results = [
            'success' => [],
            'failed' => []
        ];

        foreach ($paths as $path) {
            // 构建安全路径
            $fullPath = $this->getSafePath($path, $baseDir);

            if ($fullPath === false) {
                $results['failed'][$path] = 'Invalid path or security violation';
                continue;
            }

            try {
                if (is_dir($fullPath)) {
                    $this->deleteDirectory($fullPath);
                    $results['success'][] = $path;
                } elseif (is_file($fullPath)) {
                    if (@unlink($fullPath)) {
                        $results['success'][] = $path;
                    } else {
                        $results['failed'][$path] = 'File deletion failed';
                    }
                } else {
                    $results['failed'][$path] = 'Path does not exist';
                }
            } catch (Exception $e) {
                $results['failed'][$path] = $e->getMessage();
            }
        }

        return $results;
    }

    /**
     * 安全路径验证
     */
    private function getSafePath(string $path, ?string $baseDir): string|false
    {
        // 如果有基础目录限制
        if ($baseDir) {
            $baseDir = rtrim(realpath($baseDir), DIRECTORY_SEPARATOR);
            $fullPath = realpath($baseDir . DIRECTORY_SEPARATOR . $path);

            // 验证路径在基础目录内
            if (!$fullPath || strpos($fullPath, $baseDir) !== 0) {
                return false;
            }

            return $fullPath;
        }

        // 无基础目录限制
        return realpath($path) ?: false;
    }

    private function deleteDirectory(string $dir): void
    {
        if (!is_dir($dir)) {
            throw new RuntimeException("Directory does not exist: $dir");
        }

        // 处理符号链接
        if (is_link($dir)) {
            if (!@unlink($dir)) {
                throw new RuntimeException("Failed to remove symlink: $dir");
            }
            return;
        }

        // 尝试直接删除空目录
        @rmdir($dir);
        if (!is_dir($dir)) return;

        // 递归删除内容
        $items = @scandir($dir);
        if ($items === false) {
            throw new RuntimeException("Failed to scan directory: $dir");
        }

        $items = array_diff($items, ['.', '..']);

        foreach ($items as $item) {
            $path = $dir . DIRECTORY_SEPARATOR . $item;

            if (is_dir($path) && !is_link($path)) {
                $this->deleteDirectory($path);
            } else {
                if (!@unlink($path)) {
                    throw new RuntimeException("Failed to delete: $path");
                }
            }
        }

        // 最终删除目录
        if (!@rmdir($dir)) {
            throw new RuntimeException("Failed to remove directory: $dir");
        }
    }
	//编辑
    public function edit()
    {
        if (request()->isAjax()) {
            $param = get_params();
            if (Db::name('File')->where('id',$param['id'])->update(['name'=>$param['title']]) !== false) {
                add_log('edit', $param['id'], []);
                return to_assign(0, "操作成功");
            } else {
                return to_assign(1, "操作失败");
            }
        }
    }
	
	//移动
    public function move()
    {
        if (request()->isAjax()) {
            $group_id = get_params("group_id");
            $ids = get_params("ids");
			$idArray = explode(',', strval($ids));
			$list = [];
			foreach ($idArray as $key => $val) {
				$list[] = [
					'id' => $val,
					'group_id' => $group_id,
					'update_time' => time()
				];
			}
			$res = (new File())->saveAll($list);
            if ($res!== false) {
                return to_assign(0, "操作成功");
            } else {
                return to_assign(1, "操作失败");
            }
        }
    }
    //删除
    public function delete()
    {
        if (request()->isDelete()) {
            $ids = get_params("ids");
            $path = get_params("path") ? '/'.get_params("path") : '';
			$idArray = explode(',', strval($ids));
			$result = $this->deletePaths($idArray,$this->baseDir.$path);
			
            if(empty($result['failed'])) {
                return to_assign(0, "删除成功");
            } else {
                return to_assign(1, "删除失败");
            }
        } else {
            return to_assign(1, "错误的请求");
        }
    }

    //获取分组
    public function get_group()
    {
		$list = Db::name('FileGroup')->where([['delete_time','=',0]])->select()->toArray();
		return to_assign(0, '',$list);
	}
    //添加&编辑
    public function add_group()
    {
        if (request()->isAjax()) {
			$param = get_params();
			if($param['title'] == '全部' || $param['title']=='未分组'){
				return to_assign(1, '该分组名称已经存在');
			}
            if (!empty($param['id']) && $param['id'] > 0) {
                $count = Db::name('FileGroup')->where([['id','<>',$param['id']],['delete_time','=',0],['title','=',$param['title']]])->count();
                if ($count > 0) {
                    return to_assign(1, '该分组名称已经存在');
                }
                $res = Db::name('FileGroup')->where(['id' => $param['id']])->strict(false)->field(true)->update($param);
				if($res!=false){
					add_log('edit', $param['id'], $param);
					return to_assign(0,'编辑成功',$param['id']);
				}else{
					return to_assign(1,'操作失败');
				}
            } else {
                $count = Db::name('FileGroup')->where([['delete_time','=',0],['title','=',$param['title']]])->count();
                if ($count > 0) {
                    return to_assign(1, '该分组名称已经存在');
                }
                $gid = Db::name('FileGroup')->strict(false)->field(true)->insertGetId($param);
				if($gid!=false){
					add_log('add', $gid, $param);
					return to_assign(0,'添加成功',$gid);
				}else{
					return to_assign(1,'操作失败');
				}                
            }
        }
    }

    //删除
    public function del_group()
    {
        if (request()->isDelete()) {
            $id = get_params("id");
            $count = Db::name('File')->where(["group_id" => $id])->count();
            if ($count > 0) {
                return to_assign(1, "该分组还存在文件，请去除文件或者转移文件后再删除");
            }
            if (Db::name('FileGroup')->delete($id) !== false) {
                add_log('delete', $id, []);
                return to_assign(0, "删除成功");
            } else {
                return to_assign(1, "删除失败");
            }
        } else {
            return to_assign(1, "错误的请求");
        }
    }
}
